﻿using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Input;

namespace RG.Common.WPF.Frames
{
    [SupportDataBinding]
    public partial class EditBox
    {
        public EditBox()
        {
            InitializeComponent();
        }

        #region string ValueData
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("ValueData", typeof(string), typeof(EditBox),
                                            new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

        
        public string ValueData
        {
            set { SetValue(ValueProperty, value); }
            get { return (string)GetValue(ValueProperty); }
        }
        #endregion

        #region Custom validation rules
        public static readonly DependencyProperty CustomValidationRulesProperty =
            DependencyProperty.Register("CustomValidationRules", typeof(Collection<Func<bool>>), typeof(EditBox),
                                            new FrameworkPropertyMetadata(new Collection<Func<bool>>(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));


        public Collection<Func<bool>> CustomValidationRules
        {
            set { SetValue(CustomValidationRulesProperty, value); }
            get { return (Collection<Func<bool>>)GetValue(CustomValidationRulesProperty); }
        }
        #endregion
        
        #region string Regex
        public static readonly DependencyProperty RegexProperty =
            DependencyProperty.Register("Regex", typeof(string), typeof(EditBox),
                                            new FrameworkPropertyMetadata(""));

        public string Regex
        {
            set { SetValue(RegexProperty, value); }
            get { return (string)GetValue(RegexProperty); }
        }
        #endregion

        #region string ValidationMessage
        public static readonly DependencyProperty ValidationMessageProperty =
            DependencyProperty.Register("ValidationMessage", typeof(string), typeof(EditBox),
                                            new FrameworkPropertyMetadata(""));

        public string ValidationMessage
        {
            set { SetValue(ValidationMessageProperty, value); }
            get { return (string)GetValue(ValidationMessageProperty); }
        }
        #endregion

        #region bool IsRequired
        public static readonly DependencyProperty IsRequiredProperty =
            DependencyProperty.Register("IsRequired", typeof(bool), typeof(EditBox),
                                            new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsMeasure, OnThisChange));

        public bool IsRequired
        {
            set { SetValue(IsRequiredProperty, value); }
            get { return (bool)GetValue(IsRequiredProperty); }
        }
        #endregion

        #region bool HasOutError
        public static readonly DependencyProperty HasOutErrorProperty =
            DependencyProperty.Register("HasOutError", typeof(bool), typeof(EditBox),
                                            new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsMeasure, OnThisChange));

        private static void OnThisChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var eb = (EditBox)d;

            eb.ValidateVal(eb.ValueData);
            if (!eb.HasError)
            {
                eb.HasError = eb.HasError && eb.HasOutError;
                if (!string.IsNullOrEmpty(eb.OutMessage)) eb.ValidationMessage = eb.OutMessage;
            }
        }

        public bool HasOutError
        {
            set{ SetValue(HasOutErrorProperty, value); }
            get { return (bool)GetValue(HasOutErrorProperty); }
        }


        #endregion

        #region string OutMessage
        public static readonly DependencyProperty OutMessageProperty =
            DependencyProperty.Register("OutMessage", typeof(string), typeof(EditBox),
                                            new FrameworkPropertyMetadata(""));

        public string OutMessage
        {
            set { SetValue(OutMessageProperty, value); }
            get { return (string)GetValue(OutMessageProperty); }
        }
        #endregion

        #region ValidationType
        public static readonly DependencyProperty ValidationTypeProperty =
            DependencyProperty.Register("ValidType", typeof(ValidationType), typeof(EditBox),
                                            new FrameworkPropertyMetadata(ValidationType.None));

        public ValidationType ValidType
        {
            set { SetValue(ValidationTypeProperty, value); }
            get { return (ValidationType)GetValue(ValidationTypeProperty); }
        }
        #endregion

        #region ICommand Error
        public static readonly DependencyProperty ErrorProperty =
            DependencyProperty.Register("Error", typeof(ICommand), typeof(EditBox),
                                            new FrameworkPropertyMetadata());


        public ICommand Error
        {
            set { SetValue(ErrorProperty, value); }
            get { return (ICommand)GetValue(ErrorProperty); }
        }
        #endregion

        public string Hint { get; set; }

        public StringBuilder Message { get; set; }
        public bool HasError { get; set; }

        private void ValidateVal(string input)
        {
            bool customRules = false;
            Message = new StringBuilder();
            ValidationMessage = "";
            HasError = false;

            if (CustomValidationRules.Count > 0)
                customRules = CustomValidationRules.Where(x => !x.Invoke()).FirstOrDefault() != null ? false : true;

            if (IsRequired && string.IsNullOrEmpty(input))
            {
                Message.AppendLine("Поле обязательно для заполнения");
                ValidationMessage = "Поле обязательно для заполнения";
                HasError = true;
            }
            
            if (ValidType != ValidationType.None)
            {
                Regex RegEx;

                #region rule

                string rx;
                switch (ValidType)
                {
                    case ValidationType.FIO:
                        rx = RxConsts.RxFullFIO;
                        RegEx = new Regex(rx);
                        Hint = "введите ФИО";
                        Message.AppendLine("Содержимое поля должно начинаться с заглавной буквы и содержать только русские буквы");
                        if (!string.IsNullOrEmpty(input)) HasError = !(RegEx.IsMatch(input));
                        if (HasError)
                            ValidationMessage = string.Format("{0}{1}", "Ошибка в формате ввода ФИО",
                                                              IsRequired ? "\nПоле обязательно к заполнению" : "");
                        break;
                    case ValidationType.Name:
                        rx = RxConsts.RxSurname;
                        RegEx = new Regex(rx);
                        Hint = "введите Имя";
                        Message.AppendLine("Содержимое поля должно начинаться с заглавной буквы и содержать только русские буквы");
                        if (!string.IsNullOrEmpty(input)) HasError = !(RegEx.IsMatch(input));
                        if (HasError)
                            ValidationMessage = string.Format("{0}{1}", "Ошибка в формате ввода Имени",
                                                              IsRequired ? "\nПоле обязательно к заполнению" : "");
                        break;
                    case ValidationType.Surname:
                        rx = RxConsts.RxSurname;
                        RegEx = new Regex(rx);
                        Hint = "введите Фамилию";
                        Message.AppendLine("Содержимое поля должно начинаться с заглавной буквы и содержать только русские буквы");
                        if (!string.IsNullOrEmpty(input)) HasError = !(RegEx.IsMatch(input));
                        if (HasError)
                            ValidationMessage = string.Format("{0}{1}", "Ошибка в формате ввода Фамилии",
                                                              IsRequired ? "\nПоле обязательно к заполнению" : "");
                        break;
                    case ValidationType.Patronymic:
                        rx = RxConsts.RxSurname;
                        RegEx = new Regex(rx);
                        Hint = "введите Отчество";
                        Message.AppendLine("Содержимое поля должно начинаться с заглавной буквы и содержать только русские буквы");
                        if (!string.IsNullOrEmpty(input)) HasError = !(RegEx.IsMatch(input));
                        if (HasError)
                            ValidationMessage = string.Format("{0}{1}", "Ошибка в формате ввода Отчества",
                                                              IsRequired ? "\nПоле обязательно к заполнению" : "");
                        break;
                    case ValidationType.Phone:
                        rx = RxConsts.RxPhone;
                        RegEx = new Regex(rx);
                        Hint = "8-ХХХ-ХХХ-ХХ-ХХ для Укр. 8-ХХ-ХХ-ХХХ-ХХХ-ХХ-ХХ";
                        Message.AppendLine("Формат записи должен соответствовать следующим: 8-ХХХ-ХХХ-ХХ-ХХ или 8-ХХХХ-ХХ-ХХ-ХХ \nДля Украины 8-ХХ-ХХ-ХХХ-ХХХ-ХХ-ХХ");
                        if (!string.IsNullOrEmpty(input)) HasError = !(RegEx.IsMatch(input));
                        if (HasError)
                            ValidationMessage = string.Format("{0}\n{2}{1}", "Ошибка в формате ввода городского телефона",
                                                              IsRequired ? "\nПоле обязательно к заполнению" : "",
                                                              "Формат записи должен соответствовать следующим: 8-ХХХ-ХХХ-ХХ-ХХ или 8-ХХХХ-ХХ-ХХ-ХХ \nДля Украины 8-ХХ-ХХ-ХХХ-ХХХ-ХХ-ХХ");
                        break;
                    case ValidationType.Mobile:
                        rx = RxConsts.RxMobile;
                        RegEx = new Regex(rx);
                        Hint = "+7-ХХХ-ХХХ-ХХ-ХХ для Укр. +38-ХХХ-ХХХ–ХХ–ХХ";
                        Message.AppendLine("Формат записи должен соответствовать следующему: +7-ХХХ-ХХХ-ХХ-ХХ \nДля Украины +38-ХХХ-ХХХ–ХХ–ХХ");
                        if (!string.IsNullOrEmpty(input)) HasError = !(RegEx.IsMatch(input));
                        if (HasError)
                            ValidationMessage = string.Format("{0}\n{2}{1}", "Ошибка в формате ввода мобильного телефона",
                                                              IsRequired ? "\nПоле обязательно к заполнению" : "",
                                                              "Формат записи должен соответствовать следующему: +7-ХХХ-ХХХ-ХХ-ХХ \nДля Украины +38-ХХХ-ХХХ–ХХ–ХХ");
                        break;
                    case ValidationType.PhoneOrMobile:
                        rx = RxConsts.RxMobile;
                        RegEx = new Regex(rx);
                        var rx2 = RxConsts.RxPhone;
                        var RegEx2 = new Regex(rx2);
                        Hint = "";
                        Message.AppendLine("Формат записи должен соответствовать следующим: +7-ХХХ-ХХХ-ХХ-ХХ, 8-ХХХ-ХХХ-ХХ-ХХ или 8-ХХХХ-ХХ-ХХ-ХХ \nДля Украины +38-ХХХ-ХХХ–ХХ–ХХ или 8-ХХ-ХХ-ХХХ-ХХХ-ХХ-ХХ");
                        if (!string.IsNullOrEmpty(input)) HasError = !(RegEx.IsMatch(input)) && !(RegEx2.IsMatch(input));
                        if (HasError)
                            ValidationMessage = string.Format("{0}\n{2}{1}", "Ошибка в формате ввода дополнительного телефона",
                                                              IsRequired ? "\nПоле обязательно к заполнению" : "",
                                                              "Формат записи должен соответствовать следующим: +7-ХХХ-ХХХ-ХХ-ХХ, 8-ХХХ-ХХХ-ХХ-ХХ или 8-ХХХХ-ХХ-ХХ-ХХ \nДля Украины +38-ХХХ-ХХХ–ХХ–ХХ или 8-ХХ-ХХ-ХХХ-ХХХ-ХХ-ХХ");
                        break;
                    case ValidationType.Email:
                        rx = RxConsts.RxEmail;
                        RegEx = new Regex(rx);
                        Hint = "xxx-xxx.xxx@xxxxx.xxx";
                        Message.AppendLine("E-mail должен соответсвовать формату xxx-xxx.xxx@xxxxx.xxx");
                        if (!string.IsNullOrEmpty(input)) HasError = !(RegEx.IsMatch(input));
                        if (HasError)
                            ValidationMessage = string.Format("{0}{1}",
                                                              "Формат записи должен соответствовать следующему: xxx-xxx@xxxx.xxx ",
                                                              IsRequired ? "\nПоле обязательно к заполнению" : "");
                        break;
                    case ValidationType.Custom:
                        rx = Regex;
                        RegEx = new Regex(rx);
                        Hint = "";
                        if (!string.IsNullOrEmpty(input)) HasError = !(RegEx.IsMatch(input));
                        if (HasError)
                            ValidationMessage = string.Format("{0}{1}", "Ошибка в формате ввода",
                                                              IsRequired ? "\nПоле обязательно к заполнению" : "");
                        break;
                    case ValidationType.DigitsOnly:
                        rx = RxConsts.RxDigits;
                        RegEx = new Regex(rx);
                        Hint = "0-9";
                        if (!string.IsNullOrEmpty(input)) HasError = (RegEx.IsMatch(input));
                        if (HasError)
                            ValidationMessage = string.Format("{0}{1}", "Данное поле может содержать только числовое значение",
                                                              IsRequired ? "\nПоле обязательно к заполнению" : "");
                        break;
                    case ValidationType.Street:
                        Hint = "введите название улицы";
                        ValidationMessage = string.Format("{0}{1}", "введите название улицы без слов Ул., переулок, бульвар и  т.д.",
                                                              IsRequired ? "\nПоле обязательно к заполнению" : "");
                        break;
                    case ValidationType.City:
                        Hint = "введите название населенного пункта";
                        ValidationMessage = string.Format("{0}{1}", "введите название населенного пункта без слов Город, г., пос. и т.д.",
                                                              IsRequired ? "\nПоле обязательно к заполнению" : "");
                        break;
                    case ValidationType.PostCode:
                        rx = RxConsts.RxPostCode;
                        RegEx = new Regex(rx);
                        Hint = "ХХХХХХ";
                        if (!string.IsNullOrEmpty(input)) HasError = (!RegEx.IsMatch(input));
                        if (HasError)
                            ValidationMessage = string.Format("{0}{1}", "Индекс должен состоять из 6 символов. Для Украины перед индексом введите 0.",
                                                              IsRequired ? "\nПоле обязательно к заполнению" : "");
                        break;
                    default:
                        HasError = IsRequired;
                        return;
                }

                #endregion
            }

            if (HasOutError)
            {
                HasError = true;
                Message.AppendLine(OutMessage);
            }
            if (Error != null)
            {Error.Execute(new ValidationErrorMessage
                                  {
                                      ErrorMessage = ValidationMessage,
                                      HasError = CustomValidationRules.Count > 0 ? HasError && customRules : HasError,
                                      Sender = this
                                  });
            }
            
        }

        private void Mi_Loaded(object sender, RoutedEventArgs e)
        {
            ValidateVal(ValueData);
        }

        private void TextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
        {
            switch (ValidType)
            {
                case ValidationType.Name:
                case ValidationType.Surname:
                case ValidationType.Patronymic:
                    var idx = Tb.CaretIndex;
                    Tb.Text = !string.IsNullOrEmpty(Tb.Text)
                                  ? Tb.Text.Substring(0, 1).ToUpper() + (Tb.Text.Length > 1 ? Tb.Text.Substring(1) : "")
                                  : "";
                    Tb.CaretIndex = idx;
                    break;
            }
            ValidateVal(Tb.Text);
        }
    }

    public class ValidationErrorMessage
    {
        public string ErrorMessage { get; set; }
        public bool HasError { get; set; }
        public object Sender { get; set; }
    }

    public enum ValidationType
    {
        FIO, Name, Surname, Patronymic, Phone, Mobile, Email, DigitsOnly, Custom, PhoneOrMobile, City, Street, PostCode, District, Subway, None 
    }

    public static class RxConsts
    {
        /// <summary>
        /// проверка частей ФИО, обязательно русские буквы, 
        /// начинается с заглавной и больше никаких лишних символов
        /// </summary>
        public const string RxFIO = @"^[А-ЯЁ][а-яё]+$";

        /// <summary>
        /// проверка полностью ФИО
        /// </summary>
        public const string RxFullFIO = @"^([А-ЯЁ][а-яё]+)((-[А-ЯЁ][а-яё]+)|(\s[А-ЯЁ][а-яё]+))?\s[А-ЯЁ][а-яё]+((\s[А-ЯЁ][а-яё]+)|())";

        /// <summary>
        /// Проверка двойных фамилий и фамилий через дефис
        /// </summary>
        public const string RxSurname = @"^([А-ЯЁ][а-яё]+)((-[А-ЯЁ][а-яё]+)|(\s[А-ЯЁ][а-яё]+))?$";

        /// <summary>
        /// телефон в формате 8-ХХХ-ХХХ-ХХ-ХХ или 8-ХХХХ-ХХ-ХХ-ХХ или 8-ХХ-ХХ-ХХХ-ХХХ-ХХ-ХХ 
        /// </summary>
        public const string RxPhone = @"^8-((\d{3}-\d{3})|(\d{4}-\d{2})|(\d{2}-\d{2}-\d{3}-\d{3}))-\d{2}-\d{2}$";
        
        /// <summary>
        /// мобильный телефон в формате +7-ХХХ-ХХХ-ХХ-ХХ или +38-ХХХ-ХХХ–ХХ–ХХ
        /// </summary>
        public const string RxMobile = @"^((\+7)|(\+38))-\d{3}-\d{3}-\d{2}-\d{2}$";

        /// <summary>
        /// проверка корректности введенного адреса Email
        /// </summary>
        public const string RxEmail = @"^(([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5}))$";

        /// <summary>
        /// Только цифры
        /// </summary>
        public const string RxDigits = @"[^0-9]";

        /// <summary>
        /// Индекс
        /// </summary>
        public const string RxPostCode = @"^[0]?([0-9]{6})$"; //@"^\d{6,7}$"

    }
}
